#include "sstream"
#include "StunMessage.h"

#include "Poco/Net/DNS.h"
#include "Poco/Net/HostEntry.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/NetException.h"
#include "Poco/Net/SocketReactor.h"
#include "Poco/Net/SocketNotification.h"
#include "Poco/Net/DatagramSocket.h"

#include "NatSession.h"

using namespace cm_nat;


class SocketAsyncHandler
{
public:
    SocketAsyncHandler(Poco::Net::DatagramSocket& socket ):m_socket(socket)
    {
    }

    void handleRead(Poco::Net::ReadableNotification * sn)
    { 
        sn->release();
        char buf[100];
        int read = m_socket.receiveBytes(buf, 100);

        ByteBuffer bf(buf, read);
        p_ns->processResponse( &bf );
        

        std::cout<<"read:"<<read<<std::endl;
    }

    void handleWrite(Poco::Net::WritableNotification * wn)
    {
        int res;

        m_socket.setBlocking(true);
        

        wn->source().removeEventHandler(m_socket,
            Poco::Observer<SocketAsyncHandler,Poco::Net::WritableNotification>(*this,&SocketAsyncHandler::handleWrite));
        wn->release();
        
        
/*
        StunMessage sm;
        sm.SetType(StunMessageType::STUN_BINDING_REQUEST);
        sm.SetTransactionID("1234567890123456");
        
        ByteBuffer bf(200);
        sm.Write(&bf);

        Poco::Net::SocketAddress sa("192.168.1.99","3478");
        res = m_socket.sendTo( bf.Data(), bf.Length(), sa );
        std::cout <<"write:"<<res<<std::endl;
*/
        
        Poco::Net::SocketAddress sa("stun.l.google.com","19302");
        //Poco::Net::SocketAddress sa("192.168.1.99","3478");
        p_ns = new NatSession(&m_socket,sa);
        p_ns->bind();
    }

private:
    Poco::Net::DatagramSocket& m_socket;
    NatSession * p_ns;
};

int main()
{
    int res;

    Poco::Net::DatagramSocket skt(Poco::Net::IPAddress::IPv4);
    skt.setBlocking(false);

    Poco::Net::SocketAddress sa("192.168.1.99","3478");

    Poco::Net::SocketReactor sr;
    sr.setTimeout(Poco::Timespan(1,0,0,0,0));

    SocketAsyncHandler sah(skt);

    Poco::Observer<SocketAsyncHandler, Poco::Net::ReadableNotification > obsr( sah, &SocketAsyncHandler::handleRead);

    sr.addEventHandler(skt, obsr );

    Poco::Observer<SocketAsyncHandler, Poco::Net::WritableNotification> obsw( sah, &SocketAsyncHandler::handleWrite);

    sr.addEventHandler(skt, obsw );

    try
    {
        //skt.connect(sa);
    }
    catch (Poco::Net::NetException* e)
    {
        std::cout<<e->displayText();
    }
    catch (...) {
        std::cout<<"haha";
    }

    sr.run();
    return 0;
}